package com.xdb.sqlparser.util;

import static com.xdb.sqlparser.visitor.SQLEvalVisitor.EVAL_VALUE;

import com.xdb.sqlparser.ast.SQLExpr;
import com.xdb.sqlparser.ast.expr.SQLMethodInvokeExpr;
import com.xdb.sqlparser.visitor.SQLEvalVisitor;

public class SQLNativeFunction {

	public static boolean evalNativeFunction(SQLEvalVisitor visitor, SQLMethodInvokeExpr x, String methodName) {
		if ("mod".equals(methodName)) {
			if (x.getParameters().size() != 2) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			SQLExpr param1 = x.getParameters().get(1);
			param0.accept(visitor);
			param1.accept(visitor);

			Object param0Value = param0.getAttributes().get(EVAL_VALUE);
			Object param1Value = param1.getAttributes().get(EVAL_VALUE);
			if (param0Value == null || param1Value == null) {
				return false;
			}

			int intValue0 = SQLEvalVisitorUtils.castToInteger(param0Value);
			int intValue1 = SQLEvalVisitorUtils.castToInteger(param1Value);

			int result = intValue0 % intValue1;

			x.putAttribute(EVAL_VALUE, result);
		} else if ("abs".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			Object result;
			if (paramValue instanceof Integer) {
				result = Math.abs(((Integer) paramValue).intValue());
			} else if (paramValue instanceof Long) {
				result = Math.abs(((Long) paramValue).longValue());
			} else {
				result = SQLEvalVisitorUtils.castToDecimal(paramValue).abs();
			}

			x.putAttribute(EVAL_VALUE, result);
		} else if ("acos".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.acos(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("asin".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.asin(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("atan".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.atan(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("atan2".equals(methodName)) {
			if (x.getParameters().size() != 2) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			SQLExpr param1 = x.getParameters().get(1);
			param0.accept(visitor);
			param1.accept(visitor);

			Object param0Value = param0.getAttributes().get(EVAL_VALUE);
			Object param1Value = param1.getAttributes().get(EVAL_VALUE);
			if (param0Value == null || param1Value == null) {
				return false;
			}

			double doubleValue0 = SQLEvalVisitorUtils.castToDouble(param0Value);
			double doubleValue1 = SQLEvalVisitorUtils.castToDouble(param1Value);
			double result = Math.atan2(doubleValue0, doubleValue1);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("ceil".equals(methodName) || "ceiling".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			int result = (int) Math.ceil(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("cos".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.cos(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("sin".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.sin(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("log".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.log(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("log10".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.log10(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("tan".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.tan(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("sqrt".equals(methodName)) {
			if (x.getParameters().size() != 1) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			param0.accept(visitor);

			Object paramValue = param0.getAttributes().get(EVAL_VALUE);
			if (paramValue == null) {
				return false;
			}

			double doubleValue = SQLEvalVisitorUtils.castToDouble(paramValue);
			double result = Math.sqrt(doubleValue);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("power".equals(methodName) || "pow".equals(methodName)) {
			if (x.getParameters().size() != 2) {
				return false;
			}

			SQLExpr param0 = x.getParameters().get(0);
			SQLExpr param1 = x.getParameters().get(1);
			param0.accept(visitor);
			param1.accept(visitor);

			Object param0Value = param0.getAttributes().get(EVAL_VALUE);
			Object param1Value = param1.getAttributes().get(EVAL_VALUE);
			if (param0Value == null || param1Value == null) {
				return false;
			}

			double doubleValue0 = SQLEvalVisitorUtils.castToDouble(param0Value);
			double doubleValue1 = SQLEvalVisitorUtils.castToDouble(param1Value);
			double result = Math.pow(doubleValue0, doubleValue1);

			if (Double.isNaN(result)) {
				x.putAttribute(EVAL_VALUE, null);
			} else {
				x.putAttribute(EVAL_VALUE, result);
			}
		} else if ("pi".equals(methodName)) {
			x.putAttribute(EVAL_VALUE, Math.PI);
		} else if ("rand".equals(methodName)) {
			x.putAttribute(EVAL_VALUE, Math.random());
		} else if ("chr".equals(methodName) && x.getParameters().size() == 1) {
			SQLExpr first = x.getParameters().get(0);
			Object firstResult = SQLEvalVisitorUtils.getValue(first);
			if (firstResult instanceof Number) {
				int intValue = ((Number) firstResult).intValue();
				char ch = (char) intValue;
				x.putAttribute(EVAL_VALUE, Character.toString(ch));
			}
		} else if ("current_user".equals(methodName)) {
			x.putAttribute(EVAL_VALUE, "CURRENT_USER");
		}
		return false;
	}
}
